﻿using System;
using System.Collections.Generic;
using Patterns.Interfaces;

namespace Patterns.DataProcessing.Aggregation
{
    public class Aggregator<TDataType>
    {
        #region Fields
        private readonly Dictionary<string, IAggregate> _aggregates;
        private readonly Dictionary<string, Func<TDataType,object>> _accumulateGetters;
        #endregion

        #region Constructor
        public Aggregator()
        {
            _aggregates = new Dictionary<string, IAggregate>();
            _accumulateGetters = new Dictionary<string, Func<TDataType, object>>();
        }
        #endregion

        #region Public methods
        public void Register<TAccumulate, TTerminate, TAggregator>(string instanceId, Func<TDataType, object> accumulateGetter)
        {
            Type d1 = typeof(TAggregator);
            Type[] typeArgs = { typeof(TAccumulate), typeof(TTerminate) };
            Type constructed = d1.MakeGenericType(typeArgs);
            _aggregates[instanceId] = Activator.CreateInstance(constructed) as IAggregate;
            _accumulateGetters[instanceId] = accumulateGetter;
        }

        public void Register<TAggregator>(string instanceId, Func<TDataType, object> accumulateGetter)
        {
            _aggregates[instanceId] = Activator.CreateInstance(typeof(TAggregator)) as IAggregate;
            _accumulateGetters[instanceId] = accumulateGetter;
        }

        public TAggregator Resolve<TAggregator>(string instanceId) where TAggregator : IAggregate
        {
            IAggregate instance;
            _aggregates.TryGetValue(instanceId, out instance);
            var result = (TAggregator)instance;
            return result;
        }
        #endregion

        #region Private members
        public void Accumulate(TDataType newData)
        {
            foreach (var pair in _aggregates)
            {
                var accumulateGetter = _accumulateGetters[pair.Key];
                pair.Value.Accumulate(accumulateGetter(newData));
            }
        }
        #endregion
    }
}
